define(
		[ "ng", "$", "_", "stjs", "bootstrap", "notify" ],
		function(angular, $, _, stjs) {

			_.mixin({
				sync : function() {
					var args = [].splice.call(arguments, 0);

					var fnn = args.splice(0, 1)[0];
					_.each(args, function(asyncCall) {
						asyncCall.$promise["finally"](function() {
							if (_.every(args, '$resolved'))
								fnn();
						});
					});

				}
			});

			app = angular.module("UtilModule", [ 'UserModule' ]);
			app
					.directive(
							'epAutocomplete',
							function($parse, $http, $sce, $timeout) {
								return {
									restrict : 'EA',
									scope : {
										"id" : "@id",
										"placeholder" : "@placeholder",
										"selectedObject" : "=?",
										"searchStr":"=?",	
										"titleAs" : "@",
										"descriptionAs" : "@",
										"imageAs" : "@",
										"inputClass" : "@inputclass",
										"inputStyle":"@inputstyle",
										"userPause" : "@pause",
										"localData" : "=localdata",
										"searchFields" : "@searchfields",
										"minLengthUser" : "@minlength",
										"options" : "&",
										"valueSet" : "&"

									},
									templateUrl : 'common/templates/ep-autocomplete.html',

									link : function($scope, elem, attrs) {

										var lastSearchTerm = null;
										$scope.currentIndex = 0;
										$scope.justChanged = false;
										$scope.searchTimer = null;
										$scope.hideTimer = null;
										$scope.searching = false;
										$scope.pause = 500;
										$scope.minLength = 3;
										$scope.searchStr = null;

										if ($scope.minLengthUser && $scope.minLengthUser != "") {
											$scope.minLength = $scope.minLengthUser;
										}

										if ($scope.userPause) {
											$scope.pause = $scope.userPause;
										}

										isNewSearchNeeded = function(newTerm, oldTerm) {
											return newTerm.length >= $scope.minLength
													&& newTerm != oldTerm
										}

										var getTitle = $scope.titleAs ? $parse($scope.titleAs)
												: angular.identity;

										$scope.getTitle = function(result) {
											return $sce.trustAsHtml(getTitle(result).toString()
													.replace($scope.searchStr, "<strong>$&</strong>"));
										}

										$scope.getDescription = $scope.descriptionAs ? $parse($scope.descriptionAs)
												: angular.identity;
										$scope.getImage = $scope.imageAs ? $parse($scope.imageAs)
												: angular.identity;

										var searchTimerComplete = function(str) {
											// Begin the search

											if (str.length >= $scope.minLength) {

												$scope.options({
													query : str,
													callback : function(responseData) {
														$scope.searching = false;
														$scope.results = responseData;
													}
												});

											}

										}

										$scope.hideResults = function() {
											$scope.hideTimer = $timeout(function() {
												$scope.showDropdown = false;
											}, $scope.pause);
										};

										$scope.resetHideResults = function() {
											if ($scope.hideTimer) {
												$timeout.cancel($scope.hideTimer);
											}
											;
										};

										$scope.hoverRow = function(index) {
											console.log("index de mierda");
											if (index) {
												$scope.currentIndex = index;
											} else {
												$scope.currentIndex = 0;
											}
										}

										var keyPressed = function(event) {
											if (!(event.which == 38 || event.which == 40 || event.which == 13)) {
												if (!$scope.searchStr || $scope.searchStr == "") {
													$scope.showDropdown = false;
													lastSearchTerm = null
												} else if (isNewSearchNeeded($scope.searchStr,
														lastSearchTerm)) {
													lastSearchTerm = $scope.searchStr
													$scope.showDropdown = true;
													$scope.currentIndex = 0;
													$scope.results = [];
													if ($scope.searchTimer) {
														$timeout.cancel($scope.searchTimer);
													}
													$scope.searching = true;
													$scope.searchTimer = $timeout(function() {
														searchTimerComplete($scope.searchStr);
													}, $scope.pause);
												}
											} else {
												event.preventDefault();
											}
										}

										$scope.selectResult = function(result) {

											$scope.searchStr = lastSearchTerm = getTitle(result);
											$scope.selectedObject = result;
											$scope.showDropdown = false;
											$scope.results = [];
											$scope.valueSet({
												value : result
											});
											// $scope.$apply();
										}

										var inputField = elem.find('input');

										inputField.on('keyup', keyPressed);

										elem
												.on(
														"keyup",
														function(event) {

															if (event.which === 40) {
																if ($scope.results
																		&& ($scope.currentIndex + 1) < $scope.results.length) {
																	$scope.currentIndex++;
																	$scope.$apply();
																	event.preventDefault;
																	event.stopPropagation();
																}

																$scope.$apply();
															} else if (event.which == 38) {
																if ($scope.currentIndex >= 1) {
																	$scope.currentIndex--;
																	$scope.$apply();
																	event.preventDefault;
																	event.stopPropagation();
																}

															} else if (event.which == 13) {
																console.log("inicio");
																console.log($scope.results);
																console.log($scope);
																console.log($scope.results.length);
																console.log("fin");

																if ($scope.results
																		&& $scope.currentIndex >= 0
																		&& $scope.currentIndex < $scope.results.length) {

																	console.log("laksjhdkasjhkjahsd")
																	$scope
																			.selectResult($scope.results[$scope.currentIndex]);
																	$scope.$apply();
																	event.preventDefault;
																	event.stopPropagation();
																} else {
																	$scope.results = [];
																	$scope.$apply();
																	event.preventDefault;
																	event.stopPropagation();
																}

															} else if (event.which == 27) {
																$scope.results = [];
																$scope.showDropdown = false;
																$scope.$apply();
															} else if (event.which == 8) {
																$scope.selectedObject = null;
																$scope.$apply();
															}
														});

									}
								};
							});
			app.directive("epFileModel", function($parse, $window) {
				return {
					restrict : 'A',
					link : function(scope, elm, attrs) {

						var setter = $parse(attrs.epFileModel).assign;
						elm.bind('change', function() {
							scope.$apply(function() {
								setter(scope, elm[0].files[0])
							});

						});
					}
				}
			});

			app.factory("epModal", function() {
				var modals = {};

				var open = function(id) {
					function setModal() {
						if (!modals[id]) {
							var modal = $("[modal-id='" + id + "']");
							if (modal.prop("tagName") == "EP-MODAL-PANE") {
								setTimeout(setModal, 100);
							} else {

								modals[id] = modal;
								modals[id].modal('show');
							}
						} else {
							modals[id].modal('show');
						}
					}
					setModal();
				};
				var close = function(id) {
					if (!modals[id])
						modals[id] = $("[modal-id='" + id + "']");
					modals[id].modal('hide');
				};

				notify = function(message, level, position, timeout) {
					if (!timeout)
						timeout = 3000;
					if (!position)
						position = "bottom-center";
					if (level == "error")
						level = "danger";

					var div = $(".notifications." + position);
					if (!div.length) {
						div = document.createElement("div");
						$("body").append(div);
						div.setAttribute("class", "notifications " + position);
						div = $(".notifications." + position);
					}

					var style = "";
					if (position.indexOf("center") >= 0)
						style = "left: -50%;"
					var n = div.notify({
						message : {
							text : message
						},
						type : level,
						style : style
					});

					n.show();
					var handle = setTimeout(function() {
						n.hide()
					}, timeout);
					n.$note.hover(function() {
						clearTimeout(handle);
					}, function() {
						handle = setTimeout(function() {
							n.hide()
						}, timeout);
					});
					return n;
				};

				return {
					open : open,
					close : close,
					notify : notify
				};
			});

			app.directive("epPickList", function() {
				/**
				 * Every option in the list must always have an property id.
				 */
				return {
					templateUrl : "/common/templates/ep-pick-list.html",
					restrict : 'E',

					replace : true,
					scope : {
						options : '=',
						fieldToShow : '@',
						leftTitle : '@',
						rightTitle : '@',
						onChange : '&?'
					},
					link : function(scope, element, attrs, ctrl) {

						scope.setSelected = function(o, selected) {
							scope.onChange({
								pickable : o,
								selected : selected
							});
						};
						scope.unselected = function(o) {
							return !o.selected;
						}
						scope.selected = function(o) {
							return o.selected;
						}

					}

				};
			});

			app.directive("epSuggestionsPanel", function($parse) {

				return {
					templateUrl : "/common/templates/ep-suggestions-panel.html",
					restrict : 'E',
					replace : true,
					scope : {
						epSuggestionsProvider : '&',
						epChange : '&',
						epInput : '@',
						epSuggestionAs : '@'
					},

					link : function(scope, element, attrs, ctrl) {
						element.css({
							position : "absolute"

						})
						scope.suggestions = [];
						if (scope.epSuggestionAs) {
							scope.as = $parse(scope.epSuggestionAs);

						} else {
							scope.as = angular.identity;
						}

						var elem = $(scope.epInput);
						var key = {
							up : 38,
							down : 40,
							esc : 27,
							enter : 13,
							tab : 9
						};
						scope.selectedIndex = 0;
						elem.keydown(function(e) {
							switch (e.which) {
							case key.up:
								if (scope.selectedIndex > 0)
									scope.selectedIndex--;
								break;
							case key.down:
								if (scope.selectedIndex < scope.selectedIndex.length - 1)
									scope.selectedIndex++;
								break;
							case key.enter:

								e.preventDefault();
								break;
							}
						});

						elem.keyup(function(e) {
							switch (e.which) {
							case key.enter:
								scope.$apply(function() {
									scope.epChange({
										value : scope.suggestions[scope.selectedIndex]
									});
									scope.suggestions = [];
								});
								break;
							default:
								element.css({

									top : element.parent().height() + "px"

								});
								_.defer(function() {
									scope.epSuggestionsProvider({
										query : elem.val(),
										callback : function(results) {
											scope.suggestions = results;
										}
									});
								});
								break;

							}

						});

						elem.blur(function() {
							scope.$apply(function() {
								scope.suggestions = [];
							})
						});
					}
				};
			});
			app.directive("epModalPane", function() {
				// FIXME arreglar que con la tecla ESC no se cierre
				return {
					templateUrl : "/common/templates/ep-modal-pane.html",
					restrict : 'E',
					transclude : true,
					replace : true,
					scope : {
						title : '@',
						aceptar : '&?',
						cancelar : '&?',
						cerrar : '&?',
					},
					link : function(scope, element, attrs, ctrl) {
						scope.aceptarIn = function() {
							if (scope.aceptar)
								scope.aceptar();
						};
						scope.cerrarIn = function() {
							if (scope.cerrar)
								scope.cerrar();
							element.modal('hide');
						};
						scope.cancelarIn = function() {
							if (scope.cancelar)
								scope.cancelar();
							element.modal('hide');
						};

					}

				};
			});

			app.directive("epOpenModal", function() {

				return {

					restrict : 'A',

					scope : {
						epOpenModal : '@'
					},
					link : function(scope, element, attrs, ctrl) {

						var modal;
						element.click(function() {
							if (!modal)
								modal = $("[modal-id='" + scope.epOpenModal + "']");
							modal.modal('show');

						})
					}

				};
			});

			var FormularioPersonaScope = function(epChanging, User) {
    stjs.NgScope.call(this, "FormularioPersonaScope");
    this.epChanging = epChanging;
    this.User = User;
    this.tiposDocumento = (stjs.enums.TipoDocumento.values());
    this.sexos = (stjs.enums.Sexo.values());
};
stjs.extend(FormularioPersonaScope, stjs.NgScope, [], function(constructor, prototype) {
    prototype.tiposDocumento = null;
    prototype.sexos = null;
    prototype.epChanging = null;
    prototype.User = null;
    prototype.persona = null;
    constructor.$inject = function() {
        return ["epChanging", "User"];
    };
    prototype.buscarPersonas = function(query, callback) {
        this.User.getPersonasConDocumento(query, function(data) {
            callback(data);
        });
    };
    prototype.valueSelected = function(value) {
        console.log("valor seleccionado " + value);
        this.persona = value;
        $("#inputDocumento").attr("disabled", "disabled");
    };
    prototype.borrarPersonaSeleccionada = function() {
        this.persona = {};
        $("#inputDocumento").removeAttr("disabled");
    };
}, {tiposDocumento: {name: "Array", arguments: [{name: "Enum", arguments: ["stjs.enums.TipoDocumento"]}]}, sexos: {name: "Array", arguments: [{name: "Enum", arguments: ["stjs.enums.Sexo"]}]}, epChanging: "EPChanging", User: "EndPoints.UserEP", persona: "Models.Persona"});

			app.directive("epFormularioPersona", function(User) {
				return {
					restrict : 'E',
					replace : true,
					templateUrl : "/common/templates/ep-formulario-persona.html",
					scope : {
						persona : '='
					},
					controller : stjs.toAngularCtrl(FormularioPersonaScope)
				}
			});

			app.directive("epDraggable", function() {
				return {
					restrict : 'A',
					scope : {
						epDraggable : '='
					},
					link : function(scope, element, attrs, ctrl) {
						element.attr("draggable", true);

						element[0].ondragstart = function(ev) {

							ev.dataTransfer.setData("ep-data", JSON
									.stringify(scope.epDraggable));
						}

					}
				};
			});

			app.directive("epDroppable", function($parse) {
				return {
					restrict : 'A',
					// scope : {
					// epDroppable : '&'
					// },
					link : function(scope, element, attrs, ctrl) {

						element[0].ondragover = function(e) {
							e.preventDefault();
						}
						var func = $parse(attrs.epDroppable);

						// element[0].ondragenter = function(e) {
						//					
						// element.addClass("ep-dragover");
						// }
						// element[0].ondragleave = function(e) {
						//					
						// element.removeClass("ep-dragover");
						// }

						element[0].ondrop = function(ev) {

							// element.removeClass("ep-dragover");
							var data = angular.fromJson(ev.dataTransfer.getData("ep-data"));

							scope.$apply(function() {

								scope.item = data;
								// scope.epDroppable({
								// item : JSON.parse(data)
								// });
								func(scope);
								delete scope.item;
							});

						}

					}
				};
			});
			app.directive("epStacker", function() {
				return {
					restrict : 'E',
					scope : {
						stack : "=",
						stackName : "@",
						itemAs : "@",
						add : "&",
						remove : "&",
						see : '&?'

					},
					templateUrl : "/common/templates/ep-stacker.html",
					replace : true,
					link : function(scope, element, attrs, ctrl) {
						scope.addItem = function() {
							scope.add();
						};
					}
				};
			});

			app.directive("epAutocomplete", function() {
				// FIXME tiene lios de interaccion (de todo)
				return {
					restrict : 'A',
					templateUrl : "/common/templates/ep-combobox.html",
					replace : true,
					scope : {
						epDatasource : '=',

					},
					controller : function($scope, filterFilter) {

						$scope.optionsF = $scope.epOptions;
						var x = {};

						$scope.$watch("searchText", function() {

							x[$scope.epAs] = $scope.searchText;

							$scope.optionsF = filterFilter($scope.epOptions, x);
							if ($scope.optionsF)
								if ($scope.optionsF.length)
									$scope.selectedValue = $scope.optionsF[0];

						});

					},
					link : function(scope, element, attrs, controller) {
						scope.searchText = "";
						var input = element.find("input");
						var autocomplete = element.find("select");
						$.each(element.attr('class').split(/\s+/), function(index, item) {

							element.removeClass(item);
							input.addClass(item);
							autocomplete.addClass(item);
						});

						input.focus(function() {
							autocomplete.css({
								top : ((input.height() + 12) + "px"),
								"min-width" : (input.width() + 12) + "px"
							});
						})
						var key = {
							up : 38,
							down : 40,
							esc : 27,
							enter : 13,
							tab : 9
						}
						input.keydown(function(e) {
							if (e.which == key.up || e.which == key.down) {
								e.preventDefault();
							}
							if (e.which == key.esc)
								e.preventDefault();
						});
						input.keyup(function(e) {
							if (e.which == key.down) {
								scope.sel = true;
								autocomplete.focus();
							}
							if (e.which == key.esc)
								e.preventDefault();
						});
						scope.updateModel = function() {
							scope.epModel = scope.selectedValue;
							scope.searchText = scope.selectedValue[scope.epAs];
							scope.inp = false;
							scope.sel = false;
							scope.epOnChange();

						};
						scope.updateList = function() {
							scope.inp = true;

						};
						autocomplete.keydown(function(e) {
							if (e.which != key.down && e.which != key.up
									&& e.which != key.enter && e.which != key.tab)

								input.focus();
							if (e.which != key.down && e.which != key.up)
								e.preventDefault();
						});
						element.keypress(function(e) {

							switch (e.which) {
							case key.esc:
								scope.inp = false;
								scope.sel = false;
								e.preventDefault();
								break;
							case key.enter:
								scope.updateModel();
								e.preventDefault();
								break;
							}

							scope.$apply();
						});

					}

				};
			});

			app.factory("epLoading", function(epModal) {
				return function(promise, selector) {
					var element = $(selector);
					// En caso que haya fallado antes
					element.removeClass("ep-loading-error");

					element.addClass("ep-loading");
					promise.$promise["then"](function() {
						epModal.notify("Guardado", "success", "bottom-center", 1000);
						// element.addClass("ep-loading-success");
						setTimeout(function() {
							element.removeClass("ep-loading-success");
						}, 3000);
					});
					promise.$promise["catch"](function(e) {
						element.addClass("ep-loading-error");
						epModal.notify(e.data, "error", "bottom-center", 3000);

					});
					promise.$promise["finally"](function() {
						element.removeClass("ep-loading");
					});
				};
			});

			app.factory("epChanging", function() {
				return function(scope, timeout, fun) {
					if (scope.timer != undefined) {
						clearTimeout(scope.timer);
						delete scope.timer;
					}
					scope.timer = setTimeout(function() {
						delete scope.timer;
						fun();
					}, timeout);
				};
			});
			app.directive("epTokenizer", function($parse) {
				return {
					replace : true,
					restrict : 'A',
					templateUrl : "/common/templates/ep-tokenizer.html",
					transclude : true,
					scope : {
						epTokenizer : '=',
						epTokenAs : '@',
						epRemoveToken : '&',
					},
					link : function(scope, element, attrs, ctrl) {
						scope.as = $parse(scope.epTokenAs);
						scope.removeToken = function(token) {
							scope.epRemoveToken({
								token : token
							});
						}
					}
				}
			});

			return app;
		});
